import pygame



class Camera:
    """
    # Note: 所有计算都基于窗口左上角原坐标(0, 0)
    ## 示例
    ```
    surface = pygame.display.set_mode((1280, 720))
    camera = Camera(surface, pygame.Vector2(320, 180))
    # 创建玩家并绑定相机
    player = Player() # class Player(pygame.sprite.Sprite): pass
    camera.attach_sprite(player)

    while True:
        # 事件循环
        # ...
        camera.update()
        # 其他逻辑代码
        # ...
        surface.fill((0, 0, 0))
        camera.draw(surface)
        pygame.display.flip()
    ```
    """

    def __init__(self, surface: pygame.Surface, resolution_ratio: pygame.Vector2) -> None:
        """
        surface: 用于捕获显示画面的表面
        resolution_ratio: 捕获的画面的大小，与窗口大小成比例
        """
        self.__surface: pygame.Surface = surface
        self.__resolution_ratio: pygame.Vector2 = resolution_ratio
        self.__zoom: pygame.Vector2 = pygame.Vector2(1)
        self.__visible_region: pygame.Rect = pygame.Rect(pygame.Vector2(), self.__resolution_ratio)
        self.__limit_region: pygame.Rect = surface.get_rect()

        self.__attached_sprite: pygame.sprite.Sprite = None

    def update(self, *args, **kwargs) -> None:
        if self.__attached_sprite:
            """保持sprite在相机中央"""
            # 限制移动范围和计算可见区域矩形
            width = int(self.__resolution_ratio.x / self.__zoom.x)
            height = int(self.__resolution_ratio.y / self.__zoom.y)

            rect: pygame.Rect = self.__attached_sprite.rect
            x = rect.centerx - int(width / 2)
            y = rect.centery - int(height / 2)
            max_x = self.__limit_region.width - width + self.__limit_region.x
            max_y = self.__limit_region.height - height + self.__limit_region.y

            x = self.__limit_region.x if x < self.__limit_region.x else x
            x = max_x if x > max_x else x
            y = self.__limit_region.y if y < self.__limit_region.y else y
            y = max_y if y > max_y else y

            self.__visible_region = pygame.Rect(pygame.Vector2(x, y), pygame.Vector2(width, height))

    def draw(self, surface, *args, **kwargs) -> None:
        """
        surface: 用于显示相机捕获的画面的表面。一般传入pygame.display.set_mode()返回的surface
        """
        sub_surface = self.__surface.subsurface(self.__visible_region)
        surface.blit(pygame.transform.scale(sub_surface, surface.get_size()), (0, 0))


    def get_pos(self) -> pygame.Vector2:
        return pygame.Vector2(self.__visible_region.topleft)

    def set_pos(self, pos: pygame.Vector2) -> "Camera":
        self.__visible_region.topleft = pos
        return self
    
    def set_resolution_ratio(self, value: pygame.Vector2) -> "Camera":
        self.__resolution_ratio = value
        return self

    def set_zoom(self, zoom: pygame.Vector2) -> "Camera":
        self.__zoom = zoom
        return self
    
    def get_limit_region(self) -> pygame.Rect:
        return self.__limit_region

    def set_limit_region(self, region: pygame.Rect) -> "Camera":
        # 限制大小
        if region.width < self.__resolution_ratio.x or region.height < self.__resolution_ratio.y:
            region = pygame.Rect(region.topleft, self.__resolution_ratio)
        elif region.width > self.__surface.get_width() or region.height > self.__surface.get_height():
            region = pygame.Rect(region.topleft, self.__surface.get_size())

        x = 0 if region.x < 0 else region.x
        y = 0 if region.y < 0 else region.y
        x = self.__surface.get_width() - region.width if x + region.width else x
        y = self.__surface.get_height() - region.height if y + region.height else y
        self.__limit_region = pygame.Rect(x, y, region.width, region.height)
        return self

    def attach_sprite(self, sprite: pygame.sprite.Sprite) -> None:
        '''
        绑定sprite，相机跟随sprite移动。
        # Note: sprite必须有"rect"属性，相机跟随才会生效。
        '''
        if hasattr(sprite, "rect"):
            self.__attached_sprite = sprite
        else:
            self.__attached_sprite = None
        

        

